local lanes = require "lanes"
local config = require "config"
local datasource = config.mysql.data
local username = config.mysql.user
local password = config.mysql.auth
local host = config.mysql.host
local port = config.mysql.port

---! 获取整点时间
local function get_whole_time()
    local t = os.date("*t")
    t.min = 0
    t.sec = 0
    return os.time(t)
end

local function make_sql_cmd(last_update_time, last_update_name)
    local cmd
    if not last_update_time then
        cmd = "SELECT `update_time`, `name`, `context` FROM `user_data` WHERE `path` = 'user' ORDER BY `update_time`, `name` LIMIT 20"
    else
        if not last_update_name then
            cmd = string.format("SELECT `update_time`, `name`, `context` FROM `user_data` WHERE `path` = 'user' AND `update_time` > '%s' ORDER BY `update_time`, `name` LIMIT 20", last_update_time)
        else
            cmd = string.format("SELECT `update_time`, `name`, `context` FROM `user_data` WHERE `path` = 'user' AND CONCAT(`update_time`, '@', `name`) > '%s@%s' ORDER BY `update_time`, `name` LIMIT 20", last_update_time, last_update_name)
        end
    end
    return cmd
end

local function processor(linda)
    local json = require "cjson"
    local lanes = require "lanes"
    local mysql = require "utils.mysql"

    while true do
        if cancel_test() then
            return
        end

        local ok, failmsg = mysql.create(datasource, username, password, host, port)
        if ok then
            break
        end

        spdlog.error("datacollect", "luasql_failmsg : " .. failmsg)
        lanes.sleep(3)
    end

    local last_update_time, last_update_name
    while true do
        if cancel_test() then
            return
        end

        local ok, result = mysql.execute(function(conn)
            local cmd = "SELECT `last_update_time`, `name` FROM `snap_user` ORDER BY `last_update_time` DESC, `name` DESC LIMIT 1"
            local cur = conn:execute(cmd)
            local row = cur:fetch({}, "a")
            cur:close()
            return row
        end)

        if ok then
            if result then
                last_update_time = result.last_update_time
                last_update_name = result.name
            end
            break
        end

        lanes.sleep(3)
    end

    ---! 获取当前整点时间
    local next_whole_time = get_whole_time()

    spdlog.debug("datacollect", "[%s:%s %s] processor start ...", host, port, datasource)
    while not cancel_test() do repeat
        if os.time() < next_whole_time then
            mysql.ping()

            ---! 当前时间若小于下一整点时间，则等待执行
            lanes.sleep(3)
            break
        end

        local cmd = make_sql_cmd(last_update_time, last_update_name)
        local ok, rows = mysql.execute(function(conn)
            local rows = {}
            local cur = conn:execute(cmd)
            local row = cur:fetch({}, "a")
            while row do
                table.insert(rows, row)
                row = cur:fetch({}, "a")
            end
            cur:close()
            return rows
        end)

        if ok then
            local buffers = {}
            for _, row in ipairs(rows) do
                local ok, data = xpcall(json.decode, error_traceback, row.context or row.user_data)
                if ok then
                    ---! 人物等级
                    local level = (data.level or 1) + 1000

                    ---! 关卡等级
                    local stage_level = data.stage_level or 1

                    ---! 伙伴等级
                    local partner_level = 0
                    if type(data.partner) == "table" then
                        for _, partner in pairs(data.partner) do
                            partner_level = partner_level + (partner.level or 0)
                        end
                    end

                    ---! 装备等级
                    local equip_level = 0
                    if type(data.equip) == "table" then
                        for _, equip in pairs(data.equip) do
                            equip_level = equip_level + (equip.id or 0) % 100
                        end
                    end

                    ---! 翅膀等级
                    local wings_level = 0
                    if type(data.wings) == "table" then
                        for _, level in pairs(data.wings) do
                            wings_level = wings_level + level
                        end
                    end

                    ---! 神器等级
                    local artifact_level = 0
                    if type(data.artifact) == "table" then
                        for _, artifact in pairs(data.artifact) do
                            artifact_level = artifact_level + (artifact.level or 0)
                        end
                    end

                    ---! 技能等级
                    local skills_level = 0
                    if type(data.skills) == "table" then
                        for _, level in pairs(data.skills) do
                            skills_level = skills_level + level
                        end
                    end

                    local nickname = data.nickname or row.name
                    local context = json.encode(data.player_dbase or "")
                    local buffer = string.format("('%s', '%s', %d, %d, %d, %d, %d, %d, %d, '%s', '%s')",
                                                    row.update_time, row.name, level, stage_level, partner_level,
                                                    equip_level, wings_level, artifact_level, skills_level, nickname, context)
                    table.insert(buffers, buffer)
                end
            end

            if #buffers > 0 then
                local cmd = string.format("REPLACE INTO `snap_user` (`last_update_time`, `name`, `level`, `stage_level`, `partner_level`, `equip_level`, `wings_level`, `artifact_level`, `skills_level`, `nickname`, `context`) VALUES %s",
                                          string.join(",", buffers))
                local ok = mysql.execute(function(conn)
                    local affected_rows = conn:execute(cmd)
                    return affected_rows > 0
                end)

                if ok then
                    local row = rows[#rows]
                    last_update_time, last_update_name = row.update_time, row.name
                end

                if #buffers >= 20 then
                    ---! 继续提取数据
                    lanes.sleep(0)
                    break
                end
            end

            ---! 更新下次整点时间
            next_whole_time = get_whole_time() + 3600
        end
    until true end

    mysql.destory()
    spdlog.debug("datacollect", "[%s:%s %s] processor quit ...", host, port, datasource)
end

-------------------------------------------------------------------------------
---! 启动接口
-------------------------------------------------------------------------------
register_post_init(function()
    SERVICE_D:create("datacollect", processor)
end)
